
package ca.etsmtl.logti.log619.lab05;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.internet.MimeMessage;

import ca.etsmtl.logti.log619.lab05.analyser.Analyser;
import ca.etsmtl.logti.log619.lab05.analyser.SpamAnalyser;
import ca.etsmtl.logti.log619.lab05.filter.EmailFilter;
import ca.etsmtl.utilities.ConsoleUtilities;

/**
 * Starting point of the Anti-SPAM application as defined for the LOG-619 course.
 * 
 * @author Maxime Caumartin <maxime.caumartin.1@ens.etsmtl.ca>
 */
public class Application {
    public static final String applicationName = "619AntiSPAM";
    private static final String helpText = "This application will scan through all the email files given as a parameter"
            + "and determine if they are spam or not. The spam analysis is done by all the classes in the \\filter folder.\n";
    private static final int nbParams = 1;

    /**
     * Starting point of the application.
     * 
     * @param args
     *            Email files.
     */
    public static void main(String[] args) {
        // Check arguments
        args = ConsoleUtilities.parseParameters(args); // Removes annoying parameters

        // Basic parameter validation
        if (args.length != nbParams) {
            // The order of the parameters here is important, since if args's length is 0,
            // args[0] will throw a ArrayIndexOutOfBoundsException.
            if (args.length == 0 || ConsoleUtilities.isHelpDemand(args[0])) {
                ConsoleUtilities.printUsage(applicationName, helpText, "Email List File");
                System.exit(0);
            } else {
                if (args.length < nbParams)
                    System.err.println("Error : Not enough parameters");
                else
                    System.err.println("Error : Too many parameters");
                ConsoleUtilities.printUsage(applicationName, helpText, "Email List File");
                System.exit(1);
            }
        }

        // Presentation message
        System.out.println("Starting Analysis...");
        // Load EmailFilters

        ConsoleUtilities.debug("Scanning and loading EmailFilters...");
        EmailFilter[] filters = FilterFactory.getFilters();
        ConsoleUtilities.debugP(".", String.format("Loading EmailFilters complete. Number of classes loaded : %1$s", filters.length));

        // Create the output structure
        SpamAnalyser result = new Analyser();

        // Load email list
        try {
            BufferedReader r = new BufferedReader(new InputStreamReader(new FileInputStream(args[0])));
            String emailFilePath;
            Session mimeSession = null;
            Pattern p = Pattern.compile("(.+)\\s++(.+)");

            // For each line in the file :
            while ((emailFilePath = r.readLine()) != null) {
                BufferedInputStream curEmail = null;

                // First group is the SPAM status for the email
                // Second group is the email file's path
                Matcher m = p.matcher(emailFilePath);
                m.matches(); // Triggers the pattern test.

                // Get current file
                File f = new File(m.group(2));
                if (!f.isFile()) // probably the saved file info is relative
                    f = new File(args[0].substring(0, args[0].lastIndexOf("/")), m.group(2)); // Append the email list
                                                                                              // file's path to the
                                                                                              // actual email files

                // Load the InputStream
                curEmail = new BufferedInputStream(new FileInputStream(f));

                // Validate email
                MimeMessage msg = new MimeMessage(mimeSession, curEmail);

                // Scan email
                for (EmailFilter filter : filters) {
                    // Scan the email
                    byte res = filter.checkSpam(msg);

                    // Get the email's file
                    String msgPath = m.group(2);

                    // Get the name of the filter
                    String filterName = filter.getFilterName();

                    // Get the SPAM type
                    SpamType spamType = SpamType.valueOf(m.group(1).trim());

                    // Save the result
                    result.addResult(msgPath, filterName, spamType, res);
                }
                // Rinse and repeat

                if (result.getTotalCount() % 5 == 0)
                    ConsoleUtilities.debugP(".", "Email checked : " + result.getTotalCount());
            }

        } catch (FileNotFoundException e) {
            System.err.println(String.format("Error : %1$s is not a file or is not found", args[0]));
            ConsoleUtilities.printUsage(applicationName, helpText, "Email List File");
            System.exit(2);
        } catch (IOException e) {
            System.err.println(String.format("Error : A problem occured reading a file.\nDetails : %1$s", e.getLocalizedMessage()));
            System.exit(3);
        } catch (MessagingException e) {
            System.err.println(String.format("Error : A problem occured parsing an email file.\nDetails : %1$s", e.getLocalizedMessage()));
            System.exit(4);
        }

        // Output result
        System.out.println();
        System.out.println("Analysis complete");

        System.out.println(String.format("Number of email analyzed : %1$s", result.getTotalCount()));
        System.out.println(String.format("Number of SPAM detected : %1$s", result.getSPAMcount()));
        System.out.println(String.format("Number of false positives : %1$s", result.getFalsePositives() < 0 ? 0 : result.getFalsePositives()));
        System.out.println(String.format("Number of false negatives : %1$s", result.getFalseNegatives() < 0 ? 0 : result.getFalseNegatives()));
        //System.out.println("Details :");
        //System.out.println(result.analyserResult());

        // Exit normally
        System.exit(0);
    }
}
